package com.zhike.service.im.listener;

import com.alibaba.fastjson2.JSON;
import com.zhike.common.conf.IMBaseConf;
import com.zhike.common.model.chat.ChatMsg;
import com.zhike.common.model.chat.MsgType;
import com.zhike.common.properties.CommonPropertiesFile;
import com.zhike.common.utils.CollectionUtil;
import com.zhike.common.utils.RedisFactory;
import com.zhike.common.utils.RedisService;
import com.zhike.common.utils.ServerSession;
import com.zhike.common.utils.ServerSessionMap;
import com.zhike.mobileimsdk.server.network.MBObserver;
import com.zhike.mobileimsdk.server.utils.LocalSendHelper;
import com.zhike.mobileimsdk.server.utils.NodeUtil;
import com.zhike.mobileimsdk.server.utils.ProtocalUtil;
import com.zhike.service.im.session.SessionUtil;

import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ChatMsgConsumer {

    @Autowired
    private IMBaseConf imBaseConf;

    @Autowired
    private RedisService redisService;

    /**
     * 监听当前节点的队列
     */
    @EventListener(ApplicationReadyEvent.class)
    public void start() {
        new Thread(() -> {
            consumer();
        }).start();
    }

    private void consumer() {
        while (true) {
            Jedis jedis = null;
            try {
                jedis = redisService.getJedis();
                String lKey = NodeUtil.node(CommonPropertiesFile.host, imBaseConf.getIM_SERVER_TCP_LISTENNING_PORT());
                log.info(">>>>>> {} 监听中... >>>>>>>>>>>", lKey);
                List<String> msg = jedis.brpop(Integer.MAX_VALUE, lKey);

                log.info(">>>>>> 监听到消息: {} -> {} >>>>>>>>>>>", lKey, msg);
                if (CollectionUtil.isEmpty(msg)) {
                    continue;
                }

                if (msg.size() == 2) {
                    String msgString = msg.get(1);
                    // 解析消息并分发
                    ChatMsg chatMsg = JSON.parseObject(msgString, ChatMsg.class);
                    Channel session = checkUserLogin(chatMsg);
                    if (session != null) {
                        msgDisptcher(session, chatMsg);
                        continue;
                    }
                    log.warn("未登录成功 session is null");
                    // log.info(
                    // "[IMCORE-{}]toSession==null >>
                    // id={}的用户尝试发给客户端{}的消息：str={}因接收方的id已不在线，此次实时发送没有继续(此消息应考虑作离线处理哦).",
                    // Gateway.$(session), p.getFrom(), p.getTo(), p.getDataContent());
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
        }
    }

    /**
     * 消息分发
     *
     * @param chatMsg
     * @param jedis
     */
    private void msgDisptcher(Channel session, ChatMsg chatMsg) throws Exception {
        if (chatMsg.getMsgType() == MsgType.MSGTYPE_KICK) { // 踢人消息
            String toUid = chatMsg.getToUid();
            SessionUtil.clearUserSessionAndCloseChannel(toUid);
            return;
        }
        MBObserver mbObserver = new MBObserver() {

            @Override
            public void update(boolean sucess, Object extraObj) {
                log.info("消息发送成功");
            }

        };

        LocalSendHelper.sendData(session, ProtocalUtil.parse(chatMsg), mbObserver);
        log.info("消息已分发 {} {}", session.isActive(), chatMsg);
    }

    /**
     * 检查用户channel是否在这个节点上，如果不在，表明这个用户真的没有登录
     *
     * @param chatMsg
     * @return
     */
    private Channel checkUserLogin(ChatMsg chatMsg) {
        String toUid = chatMsg.getToUid();
        ServerSession session = ServerSessionMap.getSession(toUid);
        // 判断用户是否在本节点内有会话
        if (session != null && session.getChannel() != null) {
            return session.getChannel();
        }
        return null;
    }

}
